/*
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package org.javaongems.client.rt.awt;

/**
 * This code was clipped from the Sun JDK. The following classes are very common
 * to any math or UI library. Given that Java developers already know these
 * classes, i didnt want to re-implement them with different contracts. Can 
 * someone advise what to do. Until then I will use these copies.
 * <ul>
 * 	<li> Dimension
 * 	<li> Point
 *  <li> Rectangle
 * </ul>
 */
public class Rectangle {
	public static final int OUT_LEFT = 1;

	public static final int OUT_TOP = 2;

	public static final int OUT_RIGHT = 4;

	public static final int OUT_BOTTOM = 8;

	public int x, y, width, height;

	public Rectangle() {
		this(0, 0, 0, 0);
	}

	public Rectangle(Rectangle r) {
		this(r.x, r.y, r.width, r.height);
	}

	public Rectangle(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	public Rectangle(int width, int height) {
		this(0, 0, width, height);
	}

	public double getX() {
		return x;
	}

	public double getY() {
		return y;
	}

	public double getWidth() {
		return width;
	}

	public double getHeight() {
		return height;
	}

	public Rectangle getBounds() {
		return new Rectangle(x, y, width, height);
	}

	public void setBounds(Rectangle r) {
		setBounds(r.x, r.y, r.width, r.height);
	}

	public void setBounds(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	public Point getLocation() {
		return new Point(x, y);
	}

	public void setLocation(Point p) {
		setLocation(p.x, p.y);
	}

	public void setLocation(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void translate(int x, int y) {
		this.x += x;
		this.y += y;
	}

	public Dimension getSize() {
		return new Dimension(width, height);
	}

	public void setSize(Dimension d) {
		setSize(d.width, d.height);
	}

	public void setSize(int width, int height) {
		this.width = width;
		this.height = height;
	}

	public boolean contains(Point p) {
		return contains(p.x, p.y);
	}

	public boolean contains(int X, int Y) {
		int w = this.width;
		int h = this.height;
		if ((w | h) < 0) {
			// At least one of the dimensions is negative...
			return false;
		}
		// Note: if either dimension is zero, tests below must return false...
		int x = this.x;
		int y = this.y;
		if (X < x || Y < y) {
			return false;
		}
		w += x;
		h += y;
		// overflow || intersect
		return ((w < x || w > X) && (h < y || h > Y));

	}

	public boolean contains(Rectangle r) {
		return contains(r.x, r.y, r.width, r.height);
	}

	public boolean contains(int X, int Y, int W, int H) {
		int w = this.width;
		int h = this.height;
		if ((w | h | W | H) < 0) {
			// At least one of the dimensions is negative...
			return false;
		}
		// Note: if any dimension is zero, tests below must return false...
		int x = this.x;
		int y = this.y;
		if (X < x || Y < y) {
			return false;
		}
		w += x;
		W += X;
		if (W <= X) {
			// X+W overflowed or W was zero, return false if...
			// either original w or W was zero or
			// x+w did not overflow or
			// the overflowed x+w is smaller than the overflowed X+W
			if (w >= x || W > w)
				return false;
		} else {
			// X+W did not overflow and W was not zero, return false if...
			// original w was zero or
			// x+w did not overflow and x+w is smaller than X+W
			if (w >= x && W > w)
				return false;
		}
		h += y;
		H += Y;
		if (H <= Y) {
			if (h >= y || H > h)
				return false;
		} else {
			if (h >= y && H > h)
				return false;
		}
		return true;
	}

	public boolean intersects(Rectangle r) {
		int tw = this.width;
		int th = this.height;
		int rw = r.width;
		int rh = r.height;
		if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
			return false;
		}
		int tx = this.x;
		int ty = this.y;
		int rx = r.x;
		int ry = r.y;
		rw += rx;
		rh += ry;
		tw += tx;
		th += ty;
		// overflow || intersect
		return ((rw < rx || rw > tx) && (rh < ry || rh > ty)
				&& (tw < tx || tw > rx) && (th < ty || th > ry));
	}

	public Rectangle intersection(Rectangle r) {
		int tx1 = this.x;
		int ty1 = this.y;
		int rx1 = r.x;
		int ry1 = r.y;
		long tx2 = tx1;
		tx2 += this.width;
		long ty2 = ty1;
		ty2 += this.height;
		long rx2 = rx1;
		rx2 += r.width;
		long ry2 = ry1;
		ry2 += r.height;
		if (tx1 < rx1)
			tx1 = rx1;
		if (ty1 < ry1)
			ty1 = ry1;
		if (tx2 > rx2)
			tx2 = rx2;
		if (ty2 > ry2)
			ty2 = ry2;
		tx2 -= tx1;
		ty2 -= ty1;
		// tx2,ty2 will never overflow (they will never be
		// larger than the smallest of the two source w,h)
		// they might underflow, though...
		if (tx2 < Integer.MIN_VALUE)
			tx2 = Integer.MIN_VALUE;
		if (ty2 < Integer.MIN_VALUE)
			ty2 = Integer.MIN_VALUE;
		return new Rectangle(tx1, ty1, (int) tx2, (int) ty2);
	}

	public Rectangle union(Rectangle r) {
		int x1 = Math.min(x, r.x);
		int x2 = Math.max(x + width, r.x + r.width);
		int y1 = Math.min(y, r.y);
		int y2 = Math.max(y + height, r.y + r.height);
		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
	}

	public void add(int newx, int newy) {
		int x1 = Math.min(x, newx);
		int x2 = Math.max(x + width, newx);
		int y1 = Math.min(y, newy);
		int y2 = Math.max(y + height, newy);
		x = x1;
		y = y1;
		width = x2 - x1;
		height = y2 - y1;
	}

	public void add(Point pt) {
		add(pt.x, pt.y);
	}

	public void add(Rectangle r) {
		int x1 = Math.min(x, r.x);
		int x2 = Math.max(x + width, r.x + r.width);
		int y1 = Math.min(y, r.y);
		int y2 = Math.max(y + height, r.y + r.height);
		x = x1;
		y = y1;
		width = x2 - x1;
		height = y2 - y1;
	}

	public void grow(int h, int v) {
		x -= h;
		y -= v;
		width += h * 2;
		height += v * 2;
	}

	public boolean isEmpty() {
		return (width <= 0) || (height <= 0);
	}

	public int outcode(double x, double y) {
		/*
		 * Note on casts to double below. If the arithmetic of x+w or y+h is
		 * done in int, then we may get integer overflow. By converting to
		 * double before the addition we force the addition to be carried out in
		 * double to avoid overflow in the comparison.
		 * 
		 * See bug 4320890 for problems that this can cause.
		 */
		int out = 0;
		if (this.width <= 0) {
			out |= OUT_LEFT | OUT_RIGHT;
		} else if (x < this.x) {
			out |= OUT_LEFT;
		} else if (x > this.x + (double) this.width) {
			out |= OUT_RIGHT;
		}
		if (this.height <= 0) {
			out |= OUT_TOP | OUT_BOTTOM;
		} else if (y < this.y) {
			out |= OUT_TOP;
		} else if (y > this.y + (double) this.height) {
			out |= OUT_BOTTOM;
		}
		return out;
	}

	public boolean equals(Object obj) {
		if (obj instanceof Rectangle) {
			Rectangle r = (Rectangle) obj;
			return ((x == r.x) && (y == r.y) && (width == r.width) && (height == r.height));
		}
		return super.equals(obj);
	}

	public String toString() {
		return "Rect[x=" + x + ",y=" + y + ",width=" + width
				+ ",height=" + height + "]";
	}
}
